#include "echo.pb.h"

#include "../../server/base/Utils.h"
#include "../protorpc/RpcCodec.h"

#include "../../server/net/EventLoop.h"
#include "../../server/thread/CountDownLatch.h"
#include "../../server/logging/Logging.h"
#include "../../server/net/EventLoopThreadPool.h"
#include "../../server/net/InetAddress.h"
#include "../../server/net/TcpClient.h"
#include "../../server/net/TcpConnection.h"
#include "../protorpc/RpcChannel.h"

#include <stdio.h>
#include <unistd.h>

using namespace yhaida;

static const int kRequests = 50;

class RpcClient : Utils::nocapyable
{
public:
    RpcClient(EventLoop *loop,
              const InetAddress &serverAddr,
              CountDownLatch *allConnected,
              CountDownLatch *allFinished)
        : _client(loop, serverAddr),
          _channel(new rpc::RpcChannel()),
          _stub(get_pointer(_channel)),
          _allConnected(allConnected),
          _allFinished(allFinished),
          _count(0)
    {
        _client.setConnectionCallback(
            std::bind(&RpcClient::onConnection, this, _1));
        _client.setMessageCallback(
            std::bind(&rpc::RpcChannel::onMessage, get_pointer(_channel), _1, _2, _3));
    }

    void connect()
    {
        _client.connect();
    }

    void sendRequest()
    {
        echo::EchoRequest request;
        request.set_payload("001010");
        echo::EchoResponse *response = new echo::EchoResponse;
        _stub.Echo(NULL, &request, response, NewCallback(this, &RpcClient::replied, response));
    }

private:
    void onConnection(const TcpConnectionPtr &conn)
    {
        if (conn->connected())
        {
            _channel->setConnection(conn);
            conn->setTcpNoDelay(true);
            _allConnected->downLatch();
        }
    }

    void replied(::echo::EchoResponse *resp)
    {
        ++_count;
        if (_count < kRequests)
        {
            sendRequest();
        }
        else
        {
            LOG_INFO << "RpcClient " << this << " finished";
            _allFinished->downLatch();
        }
    }

private:
    //客户端
    TcpClient _client;
    //Rpc
    rpc::RpcChannelPtr _channel;


    
    echo::EchoService_Stub _stub;
    //其他
    CountDownLatch *_allConnected;
    CountDownLatch *_allFinished;
    int _count;
};

int main(int argc, char *argv[])
{
    LOG_INFO << "pid = " << getpid();
    if (argc > 1)
    {
        int nClients = 1;

        if (argc > 2)
        {
            nClients = atoi(argv[2]);
        }

        int nThreads = 1;

        if (argc > 3)
        {
            nThreads = atoi(argv[3]);
        }

        CountDownLatch allConnected(nClients);
        CountDownLatch allFinished(nClients);

        EventLoop loop;
        EventLoopThreadPool pool(&loop);
        pool.setThreadNum(nThreads);
        pool.start();
        InetAddress serverAddr(argv[1], 8888);

        std::vector<std::unique_ptr<RpcClient>> clients;
        for (int i = 0; i < nClients; ++i)
        {
            clients.emplace_back(new RpcClient(pool.getNextLoop(), serverAddr, &allConnected, &allFinished));
            clients.back()->connect();
        }
        allConnected.wait();
        Timestamp start(Timestamp::now());
        LOG_INFO << "all connected";
        for (int i = 0; i < nClients; ++i)
        {
            clients[i]->sendRequest();
        }
        allFinished.wait();
        Timestamp end(Timestamp::now());
        LOG_INFO << "all finished";
        double seconds = timeDifference(end, start);
        printf("%f seconds\n", seconds);
        printf("%.1f calls per second\n", nClients * kRequests / seconds);

        exit(0);
    }
    else
    {
        printf("Usage: %s host_ip numClients [numThreads]\n", argv[0]);
    }
}